/*****************************************************************************
*    Copyright (C)2003 Ali Corporation. All Rights Reserved.
*
*    File:    nim_max2120.c
*
*    Description:    This file contains MAX2120 basic function in LLD.
*    History:
*           Date            Athor        Version          Reason
*	    ============	=============	=========	=================
*		2006.05.29	Joey		0.1			Create
*****************************************************************************/

#include <sys_config.h>
#include <retcode.h>
#include <types.h>

#include <api/libc/printf.h>
#include <api/libc/string.h>
#include <bus/i2c/i2c.h>
#include <osal/osal_task.h>
#include "nim_max2120.h"

struct QPSK_TUNER_CONFIG_EXT * stMAX2120_dev_id[MAX_TUNER_SUPPORT_NUM] = {NULL};
static UINT32 stMAX2120_tuner_cnt = 0;
/*****************************************************************************
* INT32 nim_max2120_init(UINT32 para, struct QPSK_TUNER_CONFIG_EXT * ptrTuner_Config)
*
* Tuner MAX2120 Initialization
*
* Arguments:
*  Parameter1: UINT32 para		: Input Parameter
*  Parameter2: struct QPSK_TUNER_CONFIG_EXT * ptrTuner_Config		: pointer for Tuner configuration structure 
*
* Return Value: INT32			: Result
*****************************************************************************/
INT32 nim_max2120_init(UINT32* tuner_id, struct QPSK_TUNER_CONFIG_EXT * ptrTuner_Config)
{
	struct QPSK_TUNER_CONFIG_EXT * stMAX2120_ptr = NULL;
	/* check Tuner Configuration structure is available or not */
	if ( (ptrTuner_Config == NULL) || stMAX2120_tuner_cnt>=MAX_TUNER_SUPPORT_NUM)
		return ERR_FAILUE;
	stMAX2120_ptr = (struct QPSK_TUNER_CONFIG_EXT*)MALLOC(sizeof(struct QPSK_TUNER_CONFIG_EXT));
	if(!stMAX2120_ptr)
		return ERR_FAILUE;
	stMAX2120_dev_id[stMAX2120_tuner_cnt] = stMAX2120_ptr;
	MEMCPY(stMAX2120_ptr, ptrTuner_Config, sizeof(struct QPSK_TUNER_CONFIG_EXT));
	*tuner_id = stMAX2120_tuner_cnt;
	stMAX2120_tuner_cnt++;
/*	soc_printf("%s: sizeof QPSK_TUNER_CONFIG_EXT = %d\n", __FUNCTION__, sizeof(struct QPSK_TUNER_CONFIG_EXT));
	soc_printf("Base Address: 0x%x\n",stMAX2120_Config.cTuner_Base_Addr);
	soc_printf("Tuner Crystal: %dKHz\n",stMAX2120_Config.wTuner_Crystal);
	soc_printf("Output Mode: %d (1 -- Signle End; 0 -- Differential)\n",stMAX2120_Config.cTuner_Out_S_D_Sel);
*/

	return SUCCESS;
}

/*****************************************************************************
* INT32 max2120_read(UINT8 reg,UINT8* buff,UINT8 len)
*
* Tuner read register from MAX2120
*
* Arguments:
*  Parameter1: UINT8 reg		: Register offset of MAX2120
*  Parameter2: UINT8* buff		: Read data buffer
*  Parameter3: UINT8 len		: Read length
*
* Return Value: INT32			: Result
*****************************************************************************/
INT32 max2120_read(UINT32 i2c_type_id, UINT8 base_addr, UINT8 reg,UINT8* buff,UINT8 len)
{
	buff[0] = reg;
	return i2c_write_read(i2c_type_id, base_addr, buff, 1, len);
}

/*****************************************************************************
* INT32 max2120_write(UINT8 reg,UINT8* buff,UINT8 len)
*
* Tuner write register from MAX2120
*
* Arguments:
*  Parameter1: UINT8 reg		: Register offset of MAX2120
*  Parameter2: UINT8* buff		: write data buffer
*  Parameter3: UINT8 len		: write length
*
* Return Value: INT32			: Result
*****************************************************************************/
INT32 max2120_write(UINT32 i2c_type_id, UINT8 base_addr, UINT8 reg,UINT8* buff,UINT8 len)
{
	UINT8  i, buffer[8];

	if (len > 7)
	{
	    return ERR_FAILUE;
	}
	buffer[0] = reg;
	for (i = 0; i < len; i++)
	{
	    buffer[i + 1] = buff[i];
	}

	i2c_write(i2c_type_id, base_addr, buffer, len+1);
}

/*****************************************************************************
* INT32 nim_max2120_control(UINT32 freq, UINT32 sym)
*
* Tuner write operation
*
* Arguments:
*  Parameter1: UINT32 freq		: Synthesiser programmable divider
*  Parameter2: UINT32 sym			: Baseband Symbol Rate
*  Parameter3: UINT8 cp			: Charge pump current
*
* Return Value: INT32			: Result
*****************************************************************************/
INT32 nim_max2120_control(UINT32 tuner_id, UINT32 freq, UINT32 sym)
{
	INT32 i;
	UINT16 N_div;
	UINT32 N_div_F_ratio;
	UINT8 R, XDIV, VAS_DIV, VAS_DLY, CPS, ICP, D24, ADE, ADL;
	UINT8 VAS_EN, VCO_SUB, LPF, BBGAIN, LD_MUX, TURBO, CP_TST;
	UINT8 data[12], back_d[2];
	struct QPSK_TUNER_CONFIG_EXT * stMAX2120_ptr = NULL;
	if (tuner_id>=stMAX2120_tuner_cnt||tuner_id>=MAX_TUNER_SUPPORT_NUM)
		return ERR_FAILUE;
	stMAX2120_ptr = stMAX2120_dev_id[tuner_id];
	R = 4;
	N_div = (UINT16)(freq * 1000 / (stMAX2120_ptr->wTuner_Crystal / R) + 0.5);

	//default.
	N_div_F_ratio = 0x02f684;
	XDIV = 0;
	VAS_DIV = 0;
	VAS_DLY = 3;
	CPS = 1;
	ICP = 0;

	if(freq<=1125)
		D24 = 1;
	else
		D24 = 0;

	ADE = 0;
	ADL = 0;
	VAS_EN = 1;
	VCO_SUB = 0x10;
	BBGAIN = 11; //mid-scale.
	LD_MUX = 0;
	TURBO = 1;
	CP_TST = 0;

//Baseband 3db Filter caculation.
{
	float Rs_M, BW;

	//"sym" unit is Khz. we use translate to Unit Mhz here and float opertion for accuracy.	
	Rs_M = (float)(sym/1000);

	//when Rs is 0, may means auto-scan.
	if(Rs_M==0)
		Rs_M = 45.0;

	// rolloff is 35%
	BW = Rs_M*1.35 / 2;

	// 0.1dB to 3dB need about 30% transition band
	//BW = BW*1.3;

	// add 3M when Rs<6.5M
	if(Rs_M<6.5)  
		BW = BW + 6.0;

	// add 2M for LNB frequency shifting
	BW = BW + 2.0;

	// add 8% margin since fc is not very accurate	
	BW = BW*1.08;

	// Maxim LPF can be tuned form 4M to 40M, step is 0.29M
	if(BW< 4.0)
		BW = 4.0;
	if(BW>35.0)
		BW = 35.0;         

//for auto-search issue.
	//BW = 30;

	//Plus 0.5 for integer translate.
	LPF = (UINT8)((BW-4)*1000/290 + 12+0.5);
}


	//N_divider and integar mode setting. //actually the highest bit is no meaning for 2120.
	data[0]=(UINT8)((N_div>>8)&0x7f);
	data[1]=(UINT8) (N_div&0xff);

	//N_divider Fractional divider ratio setting.
	data[2]=(UINT8)((N_div_F_ratio>>16)&0x0f);
	data[3]=(UINT8)((N_div_F_ratio>>8)&0xff);
	data[4]=(UINT8)(N_div_F_ratio&0xff);

	//R divider and crystal setting.
	data[5]=(UINT8)((R&0x1f) | ((XDIV<<5)&0xe0));

	//PLL configuration.//CP select by VAS.
	data[6]=(UINT8)((VAS_DIV&0x07) | ((VAS_DLY<<3)&0x18) | CPS<<5 | ICP<<6 | D24<<7);
	
	//VCO control. disable ADC and LATCH.
	data[7]=(UINT8)((ADE&0x01) | ((ADL<<1)&0x02) | ((VAS_EN<<2)&0x04) | ((VCO_SUB<<3)&0xf8));	

	//baseband low_pass filter 3db setting.
	data[8]=LPF;	

	//baseband gain.(fixed gain by tuner setting)
	data[9]=BBGAIN;

	//shutdown: all module in normal mode.
	data[10]=0x00;	
		
	//Test modes.
	data[11]=((LD_MUX&0x07) | ((TURBO<<3)&0x08) | ((CP_TST<<5)&0xe0));	

	PRINTF("Tuner: 0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x !\n", \
		data[0],data[1],data[2],data[3],data[4],data[5],data[6],data[7],data[8],data[9],data[10],data[11]);

	for(i=0;i<12;i++)
	{
		if(max2120_write(stMAX2120_ptr->i2c_type_id, stMAX2120_ptr->cTuner_Base_Addr, i,data+i,1) != SUCCESS)
		{
			PRINTF("nim_max2120_control:I2C Write failed\n");
			return !SUCCESS;
		}
	}

	i = 0;
	while(1)
	{
		i++;
		if(i>8)
			return !SUCCESS;

		osal_task_sleep(50);

		//check PLL lock detect.
		if(max2120_read(stMAX2120_ptr->i2c_type_id, stMAX2120_ptr->cTuner_Base_Addr, 0x0c, back_d, 2) != SUCCESS)
		{
			PRINTF("max2120 read failuer! \n");
			continue;
		}

		PRINTF("max2120 CR0c is 0x%x, 0x%x !\n", back_d[0], back_d[1]);
		
		if((back_d[0]>>4)&0x01 == 0x01)
			return SUCCESS;
			
	}

	return SUCCESS;
	
}

/*****************************************************************************
* INT32 nim_max2120_status(UINT8 *lock)
*
* Tuner read operation
*
* Arguments:
*  Parameter1: UINT8 *lock		: Phase lock status
*
* Return Value: INT32			: Result
*****************************************************************************/
INT32 nim_max2120_status(UINT32 tuner_id, UINT8 *lock)
{
	if (tuner_id>=stMAX2120_tuner_cnt||tuner_id>=MAX_TUNER_SUPPORT_NUM)
	{
		*lock = 0;
		return ERR_FAILUE;
	}
	/* Because MAX2120 doesn't has this flag,return 1 directly */
	*lock = 1;
	return SUCCESS;
}

